//
//  main.m
//  CameraRPi
//
//  Created by Agrond Pham on 8/29/13.
//  Copyright (c) 2013 Khanh Hoi. All rights reserved.
//

#include "VideoCapture.h"


//temp add

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <iostream>
#include <string>
#include <pthread.h>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "SimpleIni.h"
#include "mongoose.h"

using namespace std;
using namespace cv;

Mat img;
int     is_data_ready = 0,width,height,key=0;;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


struct arg_struct {
    int arg1;
    int arg2;
};
void callbackButton(int state,void* userdata){
}
void on_mouse(int state,void* userdata){
}

// This function will be called by mongoose on every new request.
static void *callback(enum mg_event event,
                      struct mg_connection *conn) {
    const struct mg_request_info *request_info = mg_get_request_info(conn);
    
    if (event == MG_NEW_REQUEST) {
        char content[1024];
        int content_length = snprintf(content, sizeof(content),
                                      "Hello from mongoose! Remote port: %d",
                                      request_info->remote_port);
        mg_printf(conn,
                  "HTTP/1.1 200 OK\r\n"
                  "Content-Type: text/plain\r\n"
                  "Content-Length: %d\r\n"        // Always set Content-Length
                  "\r\n"
                  "%s",
                  content_length, content);
        // Mark as processed
        return NULL;
    } else {
        return NULL;
    }
}
void* getData(void* arg){
    int imgSize = (int)(img.total()*img.elemSize());
    struct arg_struct *agruments=(struct arg_struct *)arg;
    int connectSock=agruments->arg1;
    char sockData[imgSize];
    int  bytes=0;
    
    // make this thread cancellable using pthread_cancel() 
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    
    
    
    memset(sockData, 0x0, sizeof(sockData));
    //check connection
    //int currentSocketnumber=connectSock;
    //bool abc= IsConnected();
    //int y=0;
    //while((int)recv(connectSock, sockData, imgSize, 0)!=-1){
    cout<< "connectShock before loadData:"<< ntohs(connectSock) << endl;
    while(true){
        cout << "-->Waiting receipt image " << endl;
        for (int i = 0; i < imgSize; i += bytes) {
            if ((bytes = (int)recv(connectSock, sockData +i, imgSize  - i, 0)) == -1) {
                cout<<"recv failed"<<endl;
            }
        }
        // convert the received data to OpenCV's Mat format, thread safe
        pthread_mutex_lock(&mutex);
        for (int i = 0;  i < img.rows; i++) {
            for (int j = 0; j < img.cols; j++) {
                (img.row(i)).col(j) = (uchar)sockData[((img.cols)*i)+j];
            }
        }
        is_data_ready = 1;
        memset(sockData, 0x0, sizeof(sockData));
        
        pthread_mutex_unlock(&mutex);
        //y++;
    }
    
    // have we terminated yet? 
    pthread_testcancel();
	
  	// no, take a rest for a while
    usleep(1000);
    
    
    return NULL;
    
}
void* videoStream(void* arg){
    CSimpleIni cSimpleIni;
    pthread_t thread_s1;
    //set Config file
    cSimpleIni.LoadFile("config.ini");
    
    
    struct arg_struct *agruments=(struct arg_struct *)arg;
    int connectSock=agruments->arg1;
    cout<<"check connectSock."<<ntohs(connectSock)<<endl;
    
    // make this thread cancellable using pthread_cancel() 
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    
    
    
    //set width and height for screen
	int width = atoi(cSimpleIni.GetValue("screen","width",""));
	int height = atoi(cSimpleIni.GetValue("screen","height",""));
 	
    
 	
    img = Mat::zeros( height,width, CV_8UC1);
    struct arg_struct args;
    args.arg1 = connectSock;    // run the streaming server as a separate thread 
    if (pthread_create(&thread_s1, NULL, getData, &args)) {
        cout<<"pthread_create failed."<<endl;
    }
    //pthread_join(thread_s,NULL);
    cout << "\n-->Press 'q' to quit." << endl;
    //namedWindow("stream_server", CV_WINDOW_NORMAL|CV_GUI_NORMAL);
    //show frame in screen of server
    VideoWriter writeVideo("Video.avi",CV_FOURCC('D','I','V','X'),5,cvSize(width,height),false);
    // Open input
    if (!writeVideo.isOpened())
    {
        cout  << "Could not open the input video: " << endl;
    }
    while(key != 'q') {
        
        pthread_mutex_lock(&mutex);
        if (is_data_ready) {
            imshow("stream_server", img);
            is_data_ready = 0;
            writeVideo <<img;
        }
        pthread_mutex_unlock(&mutex);
        key = waitKey(10);
    }

    //cancel thread
    if (pthread_cancel(thread_s1)) {
        cout<<"pthread_cancel failed."<<endl;
    }
    
    destroyWindow("stream_server");
    
    
    
        
    // have we terminated yet? 
    pthread_testcancel();
	
  	// no, take a rest for a while
    usleep(1000);
    return NULL;
};
int main(int argc, char** argv)
{
    //start server
    
    
    
    struct mg_context *ctx;
    const char *options[] = {"listening_ports", "8080", NULL};
    
    ctx = mg_start(&callback, NULL, options);
    getchar();  // Wait until user hits "enter"
    mg_stop(ctx);
     
    
    
    //khnVideoCapture khnVideoCapture;
    //khnVideoCapture.videoCapture();
    CSimpleIni cSimpleIni;
    //set Config file
    cSimpleIni.LoadFile("config.ini");
    
    
    pthread_t thread_s;
    int     listenSock, connectSock;
    int 	listenPort;
    struct  sockaddr_in   serverAddr,  clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);
    
    listenPort = atoi(cSimpleIni.GetValue("socket","port",""));
    
    if ((listenSock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        cout<<"socket() failed." << endl;
    }
    
    serverAddr.sin_family = PF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(listenPort);
    
    if (bind(listenSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        cout<<"bind() failed" << endl;
    }
    
    if (listen(listenSock, 5) == -1) {
        cout<<"listen() failed." << endl;
    }
    
    //imgSize = (int)(img.total()*img.elemSize());
    
    
    // start receiving images 
    while(true)
    {
        cout << "-->Waiting for TCP connection on port " << listenPort << " ...\n\n";
	  	
		// accept a request from a client 
        if ((connectSock = accept(listenSock, (sockaddr*)&clientAddr, &clientAddrLen)) == -1) {
            cout << "accept() failed" << endl;
        }else{
            cout << "-->Receiving image from " << inet_ntoa(clientAddr.sin_addr) << ":" << ntohs(clientAddr.sin_port) << "...connectSock:"<< ntohs(connectSock) << endl;
            //begin for UI
            //int value = 50;
            //int value2 = 0;
            
            //cvNamedWindow("main1",CV_WINDOW_NORMAL);
            namedWindow("stream_server",CV_WINDOW_AUTOSIZE | CV_GUI_NORMAL);
            
            //createTrackbar( "track1", "stream_server", &value, 255,  NULL);//OK tested
            //char* nameb1 = "button1";
            //char* nameb2 = "button2";
            //createButton(nameb1,callbackButton,nameb1,CV_CHECKBOX,1);
            
            //createButton(nameb2,callbackButton,nameb2,CV_CHECKBOX,0);
            //createTrackbar( "track2", NULL, &value2, 255, NULL);
            //createButton("button5",callbackButton,NULL,CV_RADIOBOX,0);
            //createButton("button6",callbackButton,NULL,CV_RADIOBOX,1);
            
            //cvSetMouseCallback( "main2",on_mouse,NULL );
            

            
            //namedWindow("stream_server", CV_WINDOW_NORMAL|CV_GUI_NORMAL);
            
            
            ///end for UI
            struct arg_struct args;
            args.arg1 = connectSock;
            // run the streaming server as a separate thread
            if (pthread_create(&thread_s, NULL, videoStream, &args)) {
                cout << "pthread_create failed." << endl;
            }
		}
        
    }

    return 0;
}















